package com.nx.platform.es.common.utils;

import com.google.common.base.Joiner;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.IOUtils;
import org.yaml.snakeyaml.DumperOptions;
import org.yaml.snakeyaml.Yaml;
import org.yaml.snakeyaml.nodes.Tag;

import javax.crypto.Cipher;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.security.*;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * Created by JY on 2018/8/22.
 *
 * @Description: *
 */
public class RSAUtil {

    private final static String SPLITER = " ";

    private static PublicKey publicKey;
    private static PrivateKey privateKey;

    private static PublicKey publicKey() throws Exception {
        if (publicKey == null) {
            synchronized (RSAUtil.class) {
                if (publicKey == null) {
                    String publicKeyStr = FileUtils.readFileToString(new File(PathUtil.getConfigPath() + "/rsa.public.key"));
                    publicKey = string2PublicKey(publicKeyStr);
                }
            }
        }
        return publicKey;
    }

    private static PrivateKey privateKey() throws Exception {
        if (privateKey == null) {
            synchronized (RSAUtil.class) {
                if (privateKey == null) {
                    String privateKeyStr = FileUtils.readFileToString(new File(PathUtil.getConfigPath() + "/rsa.private.key"));
                    privateKey = string2PrivateKey(privateKeyStr);
                }
            }
        }
        return privateKey;
    }

    //生成秘钥对
    public static KeyPair getKeyPair() throws Exception {
        KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
        keyPairGenerator.initialize(1024);
        return keyPairGenerator.generateKeyPair();
    }

    //获取公钥(Base64编码)
    public static String getPublicKey(KeyPair keyPair) {
        PublicKey publicKey = keyPair.getPublic();
        byte[] bytes = publicKey.getEncoded();
        return byte2Base64(bytes);
    }

    //获取私钥(Base64编码)
    public static String getPrivateKey(KeyPair keyPair) {
        PrivateKey privateKey = keyPair.getPrivate();
        byte[] bytes = privateKey.getEncoded();
        return byte2Base64(bytes);
    }

    //将Base64编码后的公钥转换成PublicKey对象
    public static PublicKey string2PublicKey(String pubStr) throws Exception {
        byte[] keyBytes = base642Byte(pubStr);
        X509EncodedKeySpec keySpec = new X509EncodedKeySpec(keyBytes);
        KeyFactory keyFactory = KeyFactory.getInstance("RSA");
        return keyFactory.generatePublic(keySpec);
    }

    //将Base64编码后的私钥转换成PrivateKey对象
    public static PrivateKey string2PrivateKey(String priStr) throws Exception {
        byte[] keyBytes = base642Byte(priStr);
        PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(keyBytes);
        KeyFactory keyFactory = KeyFactory.getInstance("RSA");
        return keyFactory.generatePrivate(keySpec);
    }

    //公钥加密
    public static String publicEncrypt(String msg) throws Exception {
        Cipher cipher = Cipher.getInstance("RSA");
        cipher.init(Cipher.ENCRYPT_MODE, publicKey());
        byte[] bytes = cipher.doFinal(msg.getBytes());
        return byte2Base64(bytes);
    }

    //私钥解密
    public static String privateDecrypt(String msg) throws Exception {
        byte[] base642Byte = base642Byte(msg);
        Cipher cipher = Cipher.getInstance("RSA");
        cipher.init(Cipher.DECRYPT_MODE, privateKey());
        byte[] bytes = cipher.doFinal(base642Byte);
        return new String(bytes);
    }

    //字节数组转Base64编码
    public static String byte2Base64(byte[] bytes) {
        return Base64.encodeBase64String(bytes);
    }

    //Base64编码转字节数组
    public static byte[] base642Byte(String base64Key) throws IOException {
        return Base64.decodeBase64(base64Key);
    }

    public static String encodeDBConf(String dbConf) {
        List<Map<Object, Object>> config = YamlParser.parseToList(dbConf);
        List<Map<Object, Object>> convert = config.stream()
                .peek(m -> {
                    if (m.containsKey("jdbc-url")) {
                        String mysql = MoreMaps.getString(m, "jdbc-url", "").replace("jdbc:mysql://", "").replaceFirst("\\?.++", "");
                        String conf = Joiner.on(SPLITER).join(mysql, MoreMaps.getString(m, "user"), MoreMaps.getString(m, "password"));
                        try {
                            String encrypt = RSAUtil.publicEncrypt(conf);
                            m.put("jdbc-url-param", MoreMaps.getString(m, "jdbc-url").replaceFirst("jdbc:mysql.*\\?", ""));
                            m.put("cipher", encrypt);
                            m.remove("user");
                            m.remove("jdbc-url");
                            m.remove("password");
                        } catch (Exception e) {
                            e.printStackTrace();
                        }
                    }
                }).collect(Collectors.toList());
        return new Yaml().dumpAs(convert, Tag.SEQ, DumperOptions.FlowStyle.BLOCK);
    }

    public static String decodeDBConf(String dbConf) {
        List<Map<Object, Object>> configEncrypt = YamlParser.parseToList(dbConf);
        List<Map<Object, Object>> convert = configEncrypt.stream()
                .peek(m -> {
                    if (m.containsKey("cipher")) {
                        String decodeMysql = MoreMaps.getString(m, "cipher");
                        try {
                            String decrypt = RSAUtil.privateDecrypt(decodeMysql);
                            String[] params = decrypt.split(SPLITER);
                            String jdbcURL = Joiner.on("").join("jdbc:mysql://", params[0], "?", MoreMaps.getString(m, "jdbc-url-param", ""));
                            String user = params[1];
                            String password = params[2];
                            m.put("jdbc-url", jdbcURL);
                            m.put("user", user);
                            m.put("password", password);
                            m.remove("cipher");
                            m.remove("jdbc-url-param");
                        } catch (Exception e) {
                            e.printStackTrace();
                        }
                    }
                }).collect(Collectors.toList());
        return new Yaml().dumpAs(convert, Tag.SEQ, DumperOptions.FlowStyle.BLOCK);
    }
}
